package com.shujia.day14;

public class Demo04ThreadConnection {
    public static void main(String[] args) {
        /*
            TODO:
                由于同步代码块中，是对当前代码进行加锁操作，线程如果要运行，必须要获取全局唯一的一把锁
                     那么从运行结果看，线程之间执行是没有顺序的，同一个线程可能被执行多次
                那么如果想要实现多个线程之间相互执行，如何操作，并且同步代码块是以代码块加锁，能否使用线程阻塞方式
                    如果一个线程执行到某个条件时，可以通过调用方法对当前线程进行阻塞，同时唤醒其他被阻塞的线程

             注意：
                ① synchronized 也可以对当前成员方法进行加锁，加锁的是当前对象
                ② synchronized 也可以对当前静态方法进行加锁，加锁的是当前方法所属的类  => 要求自己实现
                ③ notify 作用是从因为当前对象被锁定的线程中唤醒一个线程
                ④ wait 作用是从当前对象中的当前线程进行等待阻塞，如果要被唤醒，那么必须有一个线程使用 notify 来进行
                ⑤ notify和wait必须在synchronized修饰的代码块或者方法中才能存在
                ⑥ notify和wait是属于顶级父类Object中的方法
                ⑦ notify和wait 调用的对象，必须是被加锁的
                ⑧ 当代码中先wait之后再notify代码无法完整执行，暂停了..
                    原因：

         */
//        TickRunnable tickRunnable = new TickRunnable(100);
//        new Thread(tickRunnable,"销售员1").start();
//        new Thread(tickRunnable,"销售员2").start();

        TickRunError tickRunError = new TickRunError(new TicksNum());
        new Thread(tickRunError, "销售员1").start();
        new Thread(tickRunError, "销售员2").start();
    }

    static class TickRunError implements Runnable {
        TicksNum tickNum;

        public TickRunError(TicksNum tickNum) {
            this.tickNum = tickNum;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // TODO
                synchronized (tickNum) {
                    if (tickNum.getNum() > 0) {
                        sell();
                    } else {
                        break;
                    }
                }
            }
        }

        public void sell() {
            System.out.println("当前售票员:" + Thread.currentThread().getName() + "正在销售第" + tickNum.getNum()+ "票");
            tickNum.sellTick();
            try {
                this.notify(); // 唤醒其他线程
//                tickNum.notifyAll(); // 唤醒所有线程
                tickNum.wait();  // 对当前的对象进行阻塞

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    static class TicksNum {
        static int num = 100;

        public void sellTick() {
            num -= 1;
        }

        public int getNum() {
            return num;
        }
    }


    static class TickRunnable implements Runnable {
        int tickNum;

        public TickRunnable(int tickNum) {
            this.tickNum = tickNum;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // TODO 针对TicksNumObject使用加锁操作 表示在同一时刻 有且只能使用一次该类
                if (tickNum > 0) {
                    sell();
                } else {
                    break;
                }
            }
        }

        public synchronized void sell() {
            System.out.println("当前售票员:" + Thread.currentThread().getName() + "正在销售第" + tickNum + "票");
            tickNum -= 1;
            try {
//                this.notify(); // 唤醒其他线程
                this.notifyAll(); // 唤醒所有线程
                this.wait();  // 对当前的对象进行阻塞
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


}
